package com.wh.wisdomsite.xbox.utils.file;


import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.commons.io.FileExistsException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.FileAlreadyExistsException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * 主要功能：App应用文件管理工具类
 * @Package com.wh.zsd.util
 * @author 谢泽鹏
 * @date 2014年10月8日 下午1:11:22
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2014
 * @version V1.0
 */
@SuppressWarnings("resource")
public class FileTool {

    private static final Log logger = LogFactory.getLog(FileTool.class);


    //得到当前外部存储设备的目录
    public static final String SYS_PATH = null;

    //是否剪切
    public static boolean CUT_FALG = false;

    //是否复制
    public static boolean COPY_FALG = false;

    //是否删除
    public static boolean DELETE_FALG = false;

    public static final int ERROR = -1;


    /**
     * 创建文件(系统配置下创建文件)
     *
     * @param filename 文件名称
     * @return File
     */
    public static File createFile(String filename) {
        File file = null;
        try {
            file = new File(SYS_PATH + filename);
            if (!file.exists()) {
                file.createNewFile();
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("createFile->>Exception " + e.getMessage().toString());
        }
        return file;
    }


    /**
     * 删除文件夹下的文件(如directory是个文件，将不做处理)
     *
     * @param directory
     * @return void
     */
    public static void deleteFilesByDirectory(File directory) {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            for (File file : directory.listFiles()) {
                if (file.isDirectory())
                    deleteFilesByDirectory(file);
                file.delete();
            }
            logger.info("deleteFilesByDirectory->>文件夹下的文件删除成功");
        }
    }


    /**
     * 获取文件夹大小(递归算法)
     *
     * @param file
     * @return long
     * @date 2014年10月8日 下午2:56:13
     */
    public static long getFileSize(File file) {
        long size = 0;
        if (file != null && file.exists() && file.isDirectory()) {
            File files[] = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    size = size + getFileSize(files[i]);
                } else {
                    size = size + files[i].length();
                }
            }
        }
        logger.info("getFileSize->>文件夹size : " + size);
        return size;
    }


    /**
     * 检查文件是否存在（有时间戳）
     *
     * @param path      路径
     * @param timestamp 时间戳
     * @return boolean  返回状态
     * @date 2014年11月5日 下午1:50:01
     */
    public static boolean checkFileExists(String path, String timestamp) {
        if (timestamp == null) {
            if (new File(path).exists()) {
                return true;
            }
        } else {
            File file = new File(path);
            Date fileTime = new Date(file.lastModified());
            long fileTimestamp = fileTime.getTime();
            long newTimestamp = Long.valueOf(timestamp) * 1000;
            long error = Long.valueOf(60000000);
            if (new File(path).exists() && fileTimestamp - error >= newTimestamp) {
                return true;
            }
        }
        return false;
    }


    /**
     * 获取文件名称
     *
     * @param path
     * @return String
     * @date 2014年10月8日 下午3:02:16
     */
    public static String getFileName(String path) {
        int index = path.lastIndexOf("/");
        logger.info("getFileName-->>path: " + path);
        return path.substring(index + 1);
    }


    /**
     * 删除已存在的文件
     *
     * @param path
     * @return void
     */
    public static void removeExpiredCache(String path) {
        if (checkFileExists(path)) {
            File file = new File(path);
            file.delete();
        }
    }


    /**
     * 检查文件路径是否存在
     *
     * @param path
     */
    public static boolean checkFileExists(String path) {
        if (new File(path).exists()) {
            return true;
        }
        return false;
    }


    /**
     * 一行一行读取文件，解决读取中文字符时出现乱码, 流的关闭顺序：先打开的后关，后打开的先关， 否则有可能出现java.io.IOException: Stream closed异常
     *
     * @param filePath
     * @return String
     * @throws java.io.IOException
     */
    public static String readFileByLine(String filePath) throws IOException {
        FileInputStream fis = new FileInputStream(filePath);
        InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
        BufferedReader br = new BufferedReader(isr);
        String line = "";
        String arrs = null;
        while ((line = br.readLine()) != null) {
            arrs = line;
        }
        br.close();
        isr.close();
        fis.close();
        return arrs;
    }


    /**
     * 写入文件
     *
     * @param path
     * @param value
     * @return void
     */
    public static void writeFile(String path, String value) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File(path));
        OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
        BufferedWriter bw = new BufferedWriter(osw);
        bw.write(value);
        bw.close();
        osw.close();
        fos.close();
    }


    /**
     * 根据地址获取InputStream
     *
     * @param urlStr
     * @return InputStream
     * @throws java.io.IOException
     */
    public InputStream getInputStreamByStringURL(String urlStr) {
        InputStream inputStream = null;
        try {
            URL url = new URL(urlStr);
            URLConnection urlConnection = url.openConnection();
            inputStream = urlConnection.getInputStream();
        } catch (MalformedURLException e) {
            e.printStackTrace();
            logger.error("getInputStreamByStringURL->>根据地址获取InputStream失败！" + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("getInputStreamByStringURL->>根据地址获取InputStream失败！" + e.getMessage());
        }
        return inputStream;
    }


    /**
     * 将文件转成base64 字符串
     *
     * @param path 文件路径
     * @return String  base64字符串
     */
    public static String getEncodeBase64File(String path) {
        String strings = null;
        try {
            File file = new File(path);
            FileInputStream inputFile = new FileInputStream(file);
            byte[] buffer = new byte[(int) file.length()];
            inputFile.read(buffer);
            inputFile.close();
            strings = new BASE64Encoder().encode(buffer);
            logger.info("getEncodeBase64File->>文件转换base64字符串成功！" + strings);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            logger.error("getEncodeBase64File->>文件转换base64字符串失败！" + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("getEncodeBase64File->>文件转换base64字符串失败！" + e.getMessage());
        }
        return strings;
    }


    /**
     * 将base64字符解码保存文件
     *
     * @param base64Code base64字符串
     * @param targetPath 输出目录
     * @return void
     */
    public static void getDecoderBase64File(String base64Code, String targetPath) {
        byte[] buffer;
        try {
            buffer = new BASE64Decoder().decodeBuffer(base64Code);
            FileOutputStream out = new FileOutputStream(targetPath);
            out.write(buffer);
            out.close();
            logger.info("getDecoderBase64File->>base64字符解码保存文件成功！");
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("getDecoderBase64File->>base64字符解码保存文件失败！" + e.getMessage());
        }
    }


    /**
     * 单文件剪切/目录文件剪切功能实现
     * 单文件剪切操作（1）：
     * File src = new File("F://work//s2sh.jpg");  剪切文件路径
     * File desc = new File("F://AAA//");          存放目录路径
     * falg = CutFile( src, desc, true , true);    返回文件剪切成功与失败状态(测试通过)
     * 单文件剪切操作（2）：
     * File src = new File("F://work//s2sh.jpg");  剪切文件路径
     * File src = new File("F://AAA//s2sh.jpg");   存放后全路径
     * falg = CutFile( src, desc, true , true);    返回文件剪切成功与失败状态(测试通过)
     * 文件目录剪切操作(1):
     * File src = new File("F://testB");    	      源文件所在目录
     * File desc = new File("F://AAA//testB");     文件剪切到目录全路径
     * falg = CutFile( src, desc, true , true);    返回文件剪切成功与失败状态(测试通过)
     *
     * @param src       源文件夹
     * @param desc      目标文夹
     * @param boolCover 如(源/目)文件目录同名
     * @param boolCut   如是否是剪切操作，
     * @return falg = true 文件剪切成功。falg = false 文件剪切失败。
     * @throws Exception 异常处理
     */
    public static boolean cutFile(File src, File desc, boolean boolCover, boolean boolCut) {
        try {
            //1:单文件剪切操作
            if (src.isFile()) {
                if (!desc.isFile() || boolCover)
                    //创建新文件
                    desc.createNewFile();
                //进行复制操作
                CUT_FALG = copyFile(src, desc);
                //是否是剪切操作
                if (boolCut) {
                    src.delete();
                }
            }
            //2：多文件剪切操作
            else if (src.isDirectory()) {
                desc.mkdirs();
                File[] list = src.listFiles();
                //循环向目标目录写如内容
                for (int i = 0; i < list.length; i++) {
                    String fileName = list[i].getAbsolutePath().substring(src.getAbsolutePath().length(), list[i].getAbsolutePath().length());
                    File descFile = new File(desc.getAbsolutePath() + fileName);
                    cutFile(list[i], descFile, boolCover, boolCut);
                }
                //是否是剪切操作
                if (boolCut) {
                    src.delete();
                }
            }
        } catch (Exception e) {
            CUT_FALG = false;
            e.printStackTrace();
            logger.error("cutFile->>文件剪切操作出现异常！" + e.getMessage());
        }
        return CUT_FALG;
    }


    /**
     * 单文件或多文件目录复制操作
     * 单文件复制形式1：
     * File src = new File("F://work//s2sh.jpg");  源文件全路径
     * File desc = new File("F://AAA//");          需要复制文件路径
     * falg = CopeFile(src, desc);					返回复制成功与失败状态(测试通过)
     * 单文件复制形式2：
     * File src = new File("F://work//s2sh.jpg");  源文件全路径
     * File desc = new File("F://AAA//s2sh.jpg");  需要复制文件路径
     * falg = CopeFile(src, desc);					返回复制成功与失败状态(测试通过)
     * 目录复制形式1：
     * File src = new File("F://test");     		源文件目录路径
     * File desc = new File("F://AAA//test");		复制目录下全路径
     * falg = CopeFile(src, desc);                 返回复制成功与失败状态(测试通过)
     *
     * @param src  源文件的全路径
     * @param desc 复制文件路径
     * @return falg = true 复制操作成功。falg = false 复制操作失败。
     * @throws Exception 异常处理
     */
    public static boolean copyFile(File src, File desc) {
        //创建字节流对象(输入,输出)
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        //创建文件输入流,输入流对象
        FileInputStream srcInputStream = null;
        FileOutputStream descOutputStream = null;
        //记录同文件复制数量操作
        int count = 0;
        //是否存在相同文件
        boolean boolCover = false;
        //单文件复制操作实现
        if (src.isFile()) {
            try {
                //获取需要复制下目录列表文件数组
                File[] list = desc.listFiles();
                //获取复制文件名
                String srcname = src.toString().substring(src.toString().lastIndexOf("\\") + 1, src.toString().length()).trim();
                if (null != list) {
                    if (list.length > 0) {
                        //循环判断复制目录下是否和源文名相同
                        for (int i = 0; i < list.length; i++) {
                            //获取复制目录下文件名
                            String descname = list[i].toString().substring(list[i].toString().lastIndexOf("\\") + 1, list[i].toString().length()).trim();
                            //判定复制文件名和目录文件名相同，记录重复数为1
                            if (srcname.equals(descname)) {
                                count = count + 1;
                                boolCover = true;
                            }
                            if (descname.indexOf("复件") != -1 && descname.indexOf(srcname.substring(srcname.indexOf(")") + 1, srcname.length())) != -1) {
                                count = count + 1;
                            }
                        }
                    }
                }
                //存在重复文件信息
                if (boolCover) {
                    if (count == 1) {
                        if (desc.toString().indexOf(".") != -1) {
                            //向磁盘中写入： 复件 + 复制文件名称
                            descOutputStream = new FileOutputStream(desc.toString() + "\\复件 ");
                        } else {
                            //向磁盘中写入： 复件 + 复制文件名称
                            descOutputStream = new FileOutputStream(desc.toString() + "\\复件 " + srcname);
                        }
                    } else {
                        if (desc.toString().indexOf(".") != -1) {
                            //向磁盘中写入： 复件(记录数)+ 复制文件名称
                            descOutputStream = new FileOutputStream(desc.toString() + "\\复件 (" + count + ") ");
                        } else {
                            //向磁盘中写入： 复件(记录数)+ 复制文件名称
                            descOutputStream = new FileOutputStream(desc.toString() + "\\复件 (" + count + ") " + srcname);
                        }
                    }
                } else {
                    if (desc.toString().indexOf(".") != -1) {
                        descOutputStream = new FileOutputStream(desc.toString() + "\\");
                    } else {
                        descOutputStream = new FileOutputStream(desc.toString() + "\\" + srcname);
                    }
                }
                byte[] buf = new byte[1];
                srcInputStream = new FileInputStream(src);
                bis = new BufferedInputStream(srcInputStream);
                bos = new BufferedOutputStream(descOutputStream);
                while (bis.read(buf) != -1) {
                    bos.write(buf);
                    bos.flush();
                }
                COPY_FALG = true;
            } catch (Exception e) {
                COPY_FALG = false;
                e.printStackTrace();
                logger.error("copyFile->>文件复制操作出现异常！" + e.getMessage());
            } finally {
                try {
                    if (bis != null) {
                        bis.close();
                    }
                    if (bos != null) {
                        bos.close();
                    }
                } catch (IOException e) {
                    COPY_FALG = false;
                    e.printStackTrace();
                    logger.error("copyFile->>文件复制操作，数据流关闭出现异常！" + e.getMessage());
                }
            }
        } else if (src.isDirectory()) {
            //创建目录
            desc.mkdir();
            File[] list = src.listFiles();
            //循环向目标目录写如内容
            for (int i = 0; i < list.length; i++) {
                String fileName = list[i].getAbsolutePath().substring(src.getAbsolutePath().length(), list[i].getAbsolutePath().length());
                File descFile = new File(desc.getAbsolutePath() + fileName);
                copyFile(list[i], descFile);
            }
        }
        return COPY_FALG;
    }

    /**
     * 用于对文件进行重命名操作
     * 1：重命名：FileHelper.RenameFile(new File("F:\\AAA\\A.txt"),"AA")  测试通过
     *
     * @param file 重命名文件对象
     * @param name 命名文件名称
     * @return rename_falg为true重命名成功, 为false重命名失败。
     */
    public static boolean renameFile(File file, String name) {
        String path = file.getParent();
        if (!path.endsWith(File.separator)) {
            path += File.separator;
        }
        logger.info("renameFile->>文件重命名操作成功！");
        return file.renameTo(new File(path + name));
    }


    /**
     * 用于对文件或文件夹进行删除操作
     * 1：删除文件     FileHelper.DeleteFile(new File("F:\\AAA\\A.txt"))   测试通过
     * 2：删除目录     FileHelper.DeleteFile(new File("F:\\AAA\\work"))    测试通过
     *
     * @param file 删除文件对象
     * @return delete_falg为true删除文件/目录成功,为false删除文件/目录失败。
     */
    public static boolean deleteFile(File file) {
        try {
            if (file.isFile()) {
                file.delete();
                DELETE_FALG = true;
            }
            if (file.isDirectory()) {
                File[] list = file.listFiles();
                for (int i = 0; i < list.length; i++) {
                    deleteFile(list[i]);
                }
                file.delete();
            }
        } catch (Exception e) {
            DELETE_FALG = false;
            e.printStackTrace();
            logger.info("deleteFile->>文件删除出现异常！" + e.getMessage());
        }
        return DELETE_FALG;
    }


    /**
     * 获取磁盘文件
     * @param httpServletRequest
     * @return
     */
    public static File[] getHttpFiles(HttpServletRequest httpServletRequest) {
        //获取磁盘文件
        List<File> files = new ArrayList<File>();
        try {
            //构建RequestContext
            RequestContext requestContext = new ServletRequestContext(httpServletRequest);
            //构建文件项目工厂对象
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置缓冲区大小为20M
            factory.setSizeThreshold(20 * 1024 * 1024);
            //默认缓存，大小是10K，超过10K
            //设置上传到服务器上文件的临时存放目录 (-- 非常重要，防止存放到系统盘造成系统盘空间不足)
            factory.setRepository(new File("C:\\uploadFileTemp"));

            //用工厂实例化上传组件,ServletFileUpload 用来解析文件上传请求
            ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
            //设置头部编码/最大文件大小5M
            servletFileUpload.setHeaderEncoding("UTF-8");
            //设置大小
            servletFileUpload.setSizeMax(100 * 1024 * 1024);
            //设置文件大小
            servletFileUpload.setFileSizeMax(10 * 1024 * 1024);

            //判断表单是否multipart/form-data
            logger.info("pu.file.upload 提交方式是否是？ multipart/form-data  ：" + FileUpload.isMultipartContent(requestContext));

            //解析结果放在List中
            List<FileItem> fileItems = servletFileUpload.parseRequest(httpServletRequest);
            for (FileItem fileItem : fileItems) {
                if (!fileItem.isFormField()) {
                    //文件表单提交
                    logger.info("getHttpFiles 文件表单提交 name: " + fileItem.getName());
                    logger.info("getHttpFiles 文件表单提交 getString: " + fileItem.getString("UTF-8"));
                    logger.info("getHttpFiles 文件表单提交 fieldName: " + fileItem.getFieldName());
                    logger.info("getHttpFiles 文件表单提交 contentType: " + fileItem.getContentType());
                    logger.info("getHttpFiles 文件表单提交 size: " + fileItem.getSize());
                    logger.info("getHttpFiles 文件表单提交 inputStream: " + fileItem.getInputStream());
                    //构建File文件
                    File file = new File(fileItem.getName());
                    //返回file
                    file = inputStream2File(fileItem.getInputStream(), file);
                    if(null != file){
                        //存放file
                        files.add(file);
                    }
                }
            }
        } catch (FileExistsException e){
            e.printStackTrace();
            logger.error("getHttpFiles [文件存在异常 | FileExistsException] " + e.getMessage());
        } catch (FileAlreadyExistsException e){
            e.printStackTrace();
            logger.error("getHttpFiles [文件已存在异常 | FileAlreadyExistsException] " + e.getMessage());
        } catch (FileNotFoundException e){
            e.printStackTrace();
            logger.error("getHttpFiles [文件不存在 | FileNotFoundException] " + e.getMessage());
        } catch (FileUploadException e) {
            e.printStackTrace();
            logger.error("getHttpFiles [文件上传 | FileUploadException] " + e.getMessage());
        } catch (IOException e){
            e.getMessage();
            logger.error("getHttpFiles [数据流传输 | IOException] " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("getHttpFiles [系统异常 | Exception] " + e.getMessage());
        }
            File[] fileArray = new File[files.size()];
            for(int i = 0; i < files.size(); i ++){
                fileArray[i] =  files.get(i);
            }
            return fileArray;
    }




    /**
     * 把inputStream转换成File
     * @param inputStream
     * @param file
     * @return
     * @throws Exception
     */
    public static File inputStream2File(InputStream inputStream, File file) throws Exception{
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead;
            byte[] buffer = new byte[8192];
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            logger.error("inputStream2File [文件未找到 | FileNotFoundException] " + e.getMessage());
            throw new IOException("文件未找到FileNotFoundException");
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("inputStream2File [数据流传输 | IOException] " + e.getMessage());
            throw new IOException("数据流传输异常IOException");
        }
            return file;
    }


    /**
     *
     * @param file
     * @return
     */
    public static byte[] getBytes(File file){
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(8192);
            byte[] b = new byte[8192];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            logger.error("FileNotFoundException [文件未找到 | FileNotFoundException] " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("IOException [数据流传输 | FileNotFoundException] " + e.getMessage());
        }
             return buffer;
    }

    /**
     * 获取路径下的所有文件的名称（不包括文件夹）
     * @author 丁奕
     * @param path
     * @return
     */
    public static List<String> getAllFileNames(String path){
        List<String> nameList = new ArrayList<>();
        try {
            logger.debug("getAllFileNames 根据路径获取路径下所有文件名称List，path： " + path);
            File f = new File(path);
            if (!f.exists()) {
                logger.debug("getAllFileNames 根据路径获取路径下所有文件名称 " + path + " not exists");
                return null;
            }
            File fa[] = f.listFiles();
            for (int i = 0; i < fa.length; i++) {
                File fs = fa[i];
                if (!fs.isDirectory()) {
                    nameList.add(fs.getName());
                }
            }
        }catch (Exception e) {
            logger.error("getAllFileNames 根据路径获取路径下所有文件名称 Exception： " + e.getMessage());
        }
        return nameList;
    }

    /**
     * 将字节转换为合适的单位大小
     * @param size
     * @return
     */
    public static String getPrintSize(long size) {
        //如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
        if (size < 1024) {
            return String.valueOf(size) + "B";
        } else {
            size = size / 1024;
        }
        //如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
        //因为还没有到达要使用另一个单位的时候
        //接下去以此类推
        if (size < 1024) {
            return String.valueOf(size) + "KB";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            //因为如果以MB为单位的话，要保留最后1位小数，
            //因此，把此数乘以100之后再取余
            size = size * 100;
            return String.valueOf((size / 100)) + "."
                    + String.valueOf((size % 100)) + "MB";
        } else {
            //否则如果要以GB为单位的，先除于1024再作同样的处理
            size = size * 100 / 1024;
            return String.valueOf((size / 100)) + "."
                    + String.valueOf((size % 100)) + "GB";
        }
    }
}